#ifndef GIL_FLATTENED_VIEW_HPP
#define GIL_FLATTENED_VIEW_HPP

#include <stdexcept>
#include <boost/gil/utilities.hpp>
#include "../ged/typedefs.hpp"
#include "../ged/Image.hpp"

namespace boost { namespace gil {

//! Operator "mniejszy niż" dla klasy point2<>
template <typename T> inline
bool operator<(const point2<T>& p1, const point2<T>& p2) { return (p1.x < p2.x && p1.y < p2.y); }

//! Operator "mniejszy niż lub równy" dla klasy point2<>
template <typename T> inline
bool operator<=(const point2<T>& p1, const point2<T>& p2) { return (p1.x <= p2.x && p1.y <= p2.y); }

//! Operator "większy niż" dla klasy point2<>
template <typename T> inline
bool operator>(const point2<T>& p1, const point2<T>& p2) { return (p1.x > p2.x && p1.y > p2.y); }

//! Operator "większy niż lub równy" dla klasy point2<>
template <typename T> inline
bool operator>=(const point2<T>& p1, const point2<T>& p2) { return (p1.x >= p2.x && p1.y >= p2.y); }

//! Funktor zwracający wartość piksela w spłaszczonym widoku
/*!
 * Modeluje koncept GIL PixelDereferenceAdaptorConcept
 */
template <typename View, typename Container>
class flatten_deref_fn {
public:
	typedef point2<std::ptrdiff_t> point_t;

	typedef flatten_deref_fn const_t;
	typedef typename View::value_type value_type;
	typedef value_type reference;
	typedef const value_type& const_reference;
	typedef point_t argument_type;
	typedef reference result_type;
	BOOST_STATIC_CONSTANT(bool, is_mutable=false);

	flatten_deref_fn()
		: layers_(0L) {}
	flatten_deref_fn(const Container* layers)
		: layers_(layers)
	{ }

	//! Operator umożliwiający dostęp do wartości piksela w
	//! spłaszczonym widoku
	/*!
	 * \param p współrzędne odczytywanego punktu
	 * \return wartość piksela w odczytywanym punkcie
	 */
	result_type operator()(point_t p) const
	{
		// Przesunięcie początku układu współrzędnych do
		// lewego górnego rogu warstwy tła
		typename Container::value_type bg_layer = *layers_->begin();
		p += bg_layer->getOffset();

		// Poszukiwanie najwyżej położonej warstwy, do której
		// należy dany punkt
		typename Container::const_reverse_iterator it;
		for (it = layers_->rbegin(); it != layers_->rend(); ++it) {
			typename Container::value_type layer = *it;
			point_t offset = layer->getOffset();
			point_t dims = layer->getDimensions();

			if (p >= offset && p < offset + dims) {
				return (*layer)(p - offset);
			}
		}

		// Błąd - odczyt pozycji poza widokiem
		throw std::out_of_range("flatten_deref_fn::operator()");
	}

private:
	//! Wskaźnik na stos warstw
	const Container* layers_;
};

} } // namespace boost::gil

#endif	// GIL_FLATTEN_VIEW_HPP
